package serialisation;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.ParseException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;

import reservation.Billet;
import reservation.Client;
import reservation.Trajet;
import reservation.Transport;
import data.Classe;
import data.Condition;
import data.Couchette;
import data.Repas;
import data.TypePassager;
import data.TypeTransportHelper;
import data.VilleHelper;
import errors.NegativeValueException;
import errors.StartStopException;

/**
 * Classe permettant de faire la liaison avec le fichier xml contenant la base
 * de données
 * 
 * @author Alexandre Boursier and Mamadou Diallo and Cyril Gorrieri and Emmanuel
 *         Menage
 * @version 2012.01.23
 */
public class SerializeXML extends Serializer {
	public SerializeXML(String fichier) {
		super(fichier);
	}

	/**
	 * Créer un nouveau fichier de données vierge à partir de son chemin
	 * 
	 * @return true si le fichier est créé, false sinon
	 * @throws IOException
	 * @throws NullPointerException
	 */
	public boolean creerFichier() throws IOException {
		// Obtenir une description du fichier a créer
		File f = new File(fichier);

		// Contrôler l'éventuelle existence du fichier
		if (!f.exists()) {
			// Créer le fichier
			try {
				// Mettre à jour le fichier xml
				Element racine = new Element("datas");
				Document document = new Document(racine);

				Element transports = new Element("transports");
				racine.addContent(transports);

				Element trajets = new Element("trajets");
				racine.addContent(trajets);

				XMLOutputter sortie = new XMLOutputter(Format.getPrettyFormat());
				sortie.output(document, new FileOutputStream(fichier));
			} catch (Exception e) {
				return false;
			}
		}

		return true;
	}

	public boolean store(List<Transport> transports, List<Trajet> trajets) {
		// On reinitialise le fichier
		try {
			File f = new File(fichier);
			f.delete();
			creerFichier();
		} catch (Exception e) {
			// TODO: handle exception
		}

		// Recupere le XML
		SAXBuilder sxb = new SAXBuilder();
		Document document;

		try {
			document = sxb.build(new File(fichier));
		} catch (Exception e) {
			System.out.println("Erreur lors de l'ouverture du fichier !");
			return false;
		}

		// Se placer à l'endroit de l'arborescence où l'on va mettre le nouveau
		// trajet
		Element racine = document.getRootElement();

		for (Transport t : transports) {
			store(t, racine);
		}
		for (Trajet t : trajets) {
			store(t, racine);
		}
		// Sauvegarder le fichier
		try {
			XMLOutputter sortie = new XMLOutputter(Format.getPrettyFormat());
			sortie.output(document, new FileOutputStream(fichier));
		} catch (Exception e) {
			System.out.println("Erreur lors de l'écriture dans le fichier !");
			return false;
		}
		return true;
	}

	/**
	 * Ajouter un nouveau trajet : l'ajouter dans la base de données (xml)
	 * l'ajouter à la liste des trajets
	 * 
	 * @param t
	 *            Trajet à ajouter
	 */
	public void store(Trajet t, Element racine) {
		Element lesTrajets = racine.getChild("trajets");

		// Créer l'élément correspondant au trajet à insérer
		Element trajet = t.parseXML();

		// Ajouter le nouveau trajet dans le fichier
		lesTrajets.addContent(trajet);
	}

	/**
	 * Ajouter un nouveau transport : l'ajouter dans la base de données (xml)
	 * l'ajouter à la liste des transports
	 * 
	 * @param t
	 *            Transport à ajouter
	 */
	public void store(Transport t, Element racine) {
		Element lesTransports = racine.getChild("transports");

		// Créer l'élément correspondant au transport à insérer
		Element transport = t.parseXML();

		// Ajouter le nouveau transport dans le fichier
		lesTransports.addContent(transport);
	}

	/**
	 * Charger une base de données depuis un fichier xml
	 * 
	 * @throws NegativeValueException
	 * @throws StartStopException
	 * @throws IOException
	 * @throws NullPointerException
	 * @throws NumberFormatException
	 */
	@SuppressWarnings("unchecked")
	public boolean load(LinkedList<Transport> transports,
			LinkedList<Trajet> trajets) {

		// Réinitialiser toutes les listes
		trajets.clear();
		transports.clear();

		SAXBuilder sxb = new SAXBuilder();
		Document document;

		try {
			document = sxb.build(new File(fichier));
		} catch (Exception e) {
			System.out.println("Erreur lors de l'ouverture du fichier !");
			return false;
		}

		Element racine = document.getRootElement();

		// Traiter les transports
		Element lesTransports = racine.getChild("transports");
		List<Element> listTransports = lesTransports.getChildren("transport");
		Iterator<Element> itTransport = listTransports.iterator();

		while (itTransport.hasNext()) {

			// Traiter le transport courant
			Element transportCourant = itTransport.next();

			// Récupérer l'identifiant du transport
			int idTransport = Integer.parseInt(transportCourant
					.getChildText("idTransport"));

			// Récupérer le type du transport
			String categorie = transportCourant.getChildText("categorie");

			// Récupérer la capacite du transport
			int capacite = Integer.parseInt(transportCourant
					.getChildText("capacite"));

			// Enregistrer le transport
			Transport tr;
			try {
				tr = new Transport(idTransport, TypeTransportHelper
						.getTypeTransport(categorie), capacite);
			} catch (NegativeValueException e) {
				return false;
			}

			transports.add(tr);
		}

		// Traiter les trajets
		Element lesTrajets = racine.getChild("trajets");
		List<Element> listTrajets = lesTrajets.getChildren("trajet");
		Iterator<Element> itTrajet = listTrajets.iterator();

		while (itTrajet.hasNext()) {
			
			// Traiter le trajet courant
			Element trajetCourant = itTrajet.next();

			// Récupérer l'identifiant du trajet
			int idTrajet = Integer.parseInt(trajetCourant
					.getChildText("idTrajet"));

			// Récupérer le départ du trajet
			String depart = trajetCourant.getChildText("depart");

			// Récupérer l'arrivée du trajet
			String arrivee = trajetCourant.getChildText("arrivee");

			// Récupérer l'arrivée du trajet
			String date = trajetCourant.getChildText("date");

			// Recupérer le transport
			int idTransport = Integer.parseInt(trajetCourant
					.getChildText("transportAssocie"));
			Transport tra = getTransportById(idTransport, transports);

			// Enregistrer le trajet
			Trajet tr;
			try {
				tr = new Trajet(idTrajet, VilleHelper.getVille(depart),
						VilleHelper.getVille(arrivee), Trajet
								.toFormatedDate(date), tra);
			} catch (ParseException e) {
				return false;
			}

			// Récupérer l'ensemble des voyageurs
			if (!trajetCourant.getChildren("billets").isEmpty()) {
				List<Element> listVoyageurs = trajetCourant.getChild("billets")
						.getChildren("billet");
				Iterator<Element> itVoyageurs = listVoyageurs.iterator();
				while (itVoyageurs.hasNext() && listVoyageurs.size() != 0) {

					// Traiter le voyageur courant
					Element voyageurCourant = itVoyageurs.next();

					// Récupérer l'identifiant du participant
					String ident = voyageurCourant.getChildText("identifiant");

					Client c = new Client(ident);

					String nr = voyageurCourant.getChildText("nom");
					Repas r = Repas.valueOf(voyageurCourant
							.getChildText("repas"));
					Boolean b = Boolean.valueOf(voyageurCourant
							.getChildText("fumeur"));
					TypePassager tp = TypePassager.valueOf(voyageurCourant
							.getChildText("type"));
					Classe cla = Classe.valueOf(voyageurCourant
							.getChildText("classe"));
					Condition co = Condition.valueOf(voyageurCourant
							.getChildText("condition"));
					Couchette cou = Couchette.valueOf(voyageurCourant
							.getChildText("couchette"));

					// Ajouter le billet
					tr.addBillet(new Billet(tr, c, nr, r, b, tp, cla, co, cou));
				}
			}
			trajets.add(tr);
		}
		return true;
	}

	/**
	 * Retrouver un transport par un son identifiant
	 * 
	 * @param id
	 * @return le trajet s'il est trouvé, null sinon;
	 */
	private static Transport getTransportById(int id,
			LinkedList<Transport> transports) {
		Transport found = null;
		for (Transport tmp : transports) {
			if (tmp.getIdentTransport() == id)
				found = tmp;
		}
		return found;
	}

	@Override
	public String getExtention() {
		return "xml";
	}
}
